package com.summer.data.table;

import com.summer.data.Jql;
import com.summer.data.Jsql;
import com.summer.data.config.Config;
import com.summer.data.dialect.Dialect;
import com.summer.data.jql.Context;
import com.summer.data.jql.behavior.FilterAble;
import com.summer.data.jql.concept.Field;
import com.summer.data.jql.concept.Value;
import com.summer.data.jql.concept.exp.BetweenFilterExp;
import com.summer.data.jql.concept.exp.OP;
import com.summer.data.jql.concept.exp.OpFilterExp;
import com.summer.data.jql.concept.exp.OrderExp;
import com.summer.data.jql.concept.exp.filter_in.ColumnInJqlExp;
import com.summer.data.jql.concept.exp.filter_in.ColumnInJsqlExp;
import com.summer.data.jql.concept.exp.filter_in.ColumnInValuesExp;
import com.summer.data.jql.concept.exp.filter_null.ColumnIsNotNull;
import com.summer.data.jql.concept.exp.filter_null.ColumnIsNull;
import com.summer.data.annotation.column.ColumnName;

import java.sql.JDBCType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.function.Function;

import static com.summer.data.jql.concept.exp.OP.*;

/**
 * @author 345480567@qq.com
 */

public abstract class BaseColumn<T> implements Field<T> {

    public BaseTable table;

    public String colName;

    public T value;

    private boolean hasChanged = false;

    public int length;

    public BaseColumn(String colName) {
        this.colName = colName;
    }

//    private Object entity;

//    public BaseColumn(String colName, Object entity) {
//        Table tableNameAnnotation = entity.getClass().getAnnotation(Table.class);
//        System.out.println(tableNameAnnotation);
//        this.entity = entity;
//    }

    public void setValue(T value) {
        hasChanged = true;
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public boolean isNeedInsert() {
        return hasChanged;
    }

    public boolean isNeedUpdate() {
        return hasChanged;
    }

    public abstract void fetchNotNullValue(ResultSet resultSet, Integer index) throws SQLException;

    public FilterAble eq(Field<T> field) {
        return op(EQ, field);
    }

    public FilterAble eq(T value) {
        return op(EQ, new Value<T>(value));
    }

    public FilterAble notEq(Field<T> field) {
        return op(NOTEQ, field);
    }

    public FilterAble notEq(T value) {
        return op(NOTEQ, new Value<T>(value));
    }

    public FilterAble lt(Field<T> field) {
        return op(LT, field);
    }

    public FilterAble lt(T value) {
        return op(LT, new Value<T>(value));
    }

    public FilterAble gt(Field<T> field) {
        return op(GT, field);
    }

    public FilterAble gt(T value) {
        return op(GT, new Value<T>(value));
    }

    public FilterAble ltEq(Field<T> field) {
        return op(LT_EQ, field);
    }

    public FilterAble ltEq(T value) {
        return op(LT_EQ, new Value<T>(value));
    }

    public FilterAble gtEq(Field<T> field) {
        return op(GT_EQ, field);
    }

    public FilterAble gtEq(T value) {
        return op(GT_EQ, new Value<T>(value));
    }

    private FilterAble op(OP op, Field<T> field) {
        return new OpFilterExp(op, this, field);
    }

    public FilterAble isNull() {
        return new ColumnIsNull(this);
    }

    public FilterAble isNotNull() {
        return new ColumnIsNotNull(this);
    }

    public FilterAble between(Field<T> st, Field<T> ed) {
        return new BetweenFilterExp(this, st, ed);
    }

    public FilterAble between(T st, T ed) {
        return new BetweenFilterExp(this, new Value<T>(st), new Value<T>(ed));
    }

    public FilterAble in(T... values) {
        return new ColumnInValuesExp<T>(this, values);
    }

    public FilterAble in(Jsql jsql) {
        return new ColumnInJsqlExp(this, jsql);
    }

    public FilterAble in(Jql jql) {
        return new ColumnInJqlExp(this, jql);
    }

//    public OrderExp asc() {
//        return new OrderExp(this);
//    }
//
//    public OrderExp desc() {
//        return new OrderExp(this, OrderExp.ORDER_TYPE.DESC);
//    }

    @Override
    public Jsql toFieldSql(Context ctx, Dialect dialect) {
        if (table == null) {
            return new Jsql(colName);
        }
        // 在select中，需要加上 表名.
        // 在 cud 中不需要
        if (ctx.isQuery()) {
            String alias = ctx.getAlias(table);
            if (alias == null || alias.trim().isEmpty()) {
                return Jsql.format("%s.%s", table.getTableName(), colName);
            } else {
                return Jsql.format("%s.%s", alias, colName);
            }
        } else {
            return Jsql.format("%s", colName);
        }
    }

    public static String getFieldColumnName(java.lang.reflect.Field field) {
        // todo: add Cache
        // 字段名
        String fieldName = field.getName();
        ColumnName columnName = field.getAnnotation(ColumnName.class);
        // 如果有 columnName 注解，使用注解的值作为字段名
        if (columnName != null && !columnName.value().isEmpty()) {
            fieldName = columnName.value();
        }

        // 读取全局配置，确定最后的字段名
        Config config = Config.getConfig();
        Function<String, String> fieldName2DbName = config.getFieldName2DbName();
        if (fieldName2DbName != null) {
            fieldName = fieldName2DbName.apply(fieldName);
        }

        return fieldName;
    }
}
